package com.ftqh.security.login;

import com.ftqh.core.web.config.StaticResourceProperties;
import com.ftqh.security.service.SecurityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.web.context.HttpRequestResponseHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.savedrequest.DefaultSavedRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@Controller
@SessionAttributes("authorizationRequest")
public class LoginController {

    @Autowired
    private StaticResourceProperties staticResourceProperties;

    @Autowired
    private SecurityService securityService;
    @Autowired
    private ClientDetailsService clientDetailsService;
    @Autowired
    private HttpSessionSecurityContextRepository sessionRepository;

    @GetMapping("/login")
    public ModelAndView login() {
        ModelAndView modelAndView = getModelAndViewInstance("login");
        return modelAndView;
    }

    @PostMapping("/login/post")
    public ModelAndView login(HttpServletRequest request, HttpServletResponse response) {
        HttpRequestResponseHolder responseHolder = new HttpRequestResponseHolder(request, response);
        sessionRepository.loadContext(responseHolder);
        ModelAndView modelAndView = getModelAndViewInstance("authorize");
        try {
            securityService.login(request.getParameter("username"),request.getParameter("password"));
        } catch (BadCredentialsException ex) {
            // The user couldn't be authenticated, redirect back to login
            ex.printStackTrace();
            modelAndView.setViewName("redirect:/login");
            return modelAndView;
        }

        // Get the default saved request from session
        DefaultSavedRequest defaultSavedRequest = ((DefaultSavedRequest) request.getSession().getAttribute("SPRING_SECURITY_SAVED_REQUEST"));
        if(null == defaultSavedRequest){
            modelAndView.setViewName("redirect:/welcome");
        }else {
            // Generate an authorization parameter map for the token request
            Map<String, String> authParams = getAuthParameters(defaultSavedRequest);

            // Create the authorization request and put it in the view model
            AuthorizationRequest authRequest = new DefaultOAuth2RequestFactory(clientDetailsService).createAuthorizationRequest(authParams);
            authRequest.setAuthorities(AuthorityUtils.createAuthorityList("ROLE_USER", "ROLE_ADMIN"));
            sessionRepository.saveContext(SecurityContextHolder.getContext(), responseHolder.getRequest(), responseHolder.getResponse());

            modelAndView.addObject("authorizationRequest", authRequest);
        }
        // Return the token authorization view
        return modelAndView;
    }
    @RequestMapping(value = {"/", "/welcome"})
    public String welcome(Model model) {
        return "welcome";
    }
    /**
     * Generate an authorization parameter map from the session's token request
     * @param defaultSavedRequest the default saved request from the session
     * @return a map of parameters containing the OAuth2 request details
     */
    private Map<String, String> getAuthParameters(DefaultSavedRequest defaultSavedRequest) {
        Map<String, String> authParams = new HashMap<>();

        authParams.put(OAuth2Utils.CLIENT_ID,
                defaultSavedRequest.getParameterMap().get(OAuth2Utils.CLIENT_ID)[0]);

        authParams.put(OAuth2Utils.REDIRECT_URI,
                defaultSavedRequest.getParameterMap().get(OAuth2Utils.REDIRECT_URI)[0]);

        if(defaultSavedRequest.getParameterMap().get(OAuth2Utils.STATE) != null) {
            authParams.put(OAuth2Utils.STATE,
                    defaultSavedRequest.getParameterMap().get(OAuth2Utils.STATE)[0]);
        }

        authParams.put(OAuth2Utils.RESPONSE_TYPE, "code");
        authParams.put(OAuth2Utils.USER_OAUTH_APPROVAL, "true");
        authParams.put(OAuth2Utils.GRANT_TYPE, "authorization_code");
        return authParams;
    }

    private ModelAndView getModelAndViewInstance(String viewName){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName(viewName);
        modelAndView.addObject("staticResourceUrl", staticResourceProperties.getStaticResourceUrl());
        return modelAndView;
    }

}
